package FIT::Document::Transformer;

use FIT::Class;
use Sub::Uplevel;
use FIT::Runner::Tally::Types;
use aliased 'FIT::Util::ClassLoader';
use FIT::Document::Transformer::Error;
use base 'Object::Declare';

my @Transformers = (tally_types, qw(
    Void Green Red Blue
    Note
    AddRows AddColumns AddFooterCell AddLink
    AddBodyHeader AddSummaryTable
    Replace
));

my $Prefix     = 'FIT::Document::Transformer::';
my $Mapping    = { map { $_ => "$Prefix$_" } @Transformers };
my $Declarator = 'transform';
my $Copula     = {was => '', are => '', is => ''};
my $Method     = "_$Declarator";

# TODO: lazy load these, use Class::MOP::load_class
ClassLoader->load("$Prefix$_") for @Transformers;

sub import() {
	my $from = caller;
	push @_, (
		mapping    => $Mapping,
		declarator => 'dont_want_no_declarator',
		copula     => $Copula,
	);
	{
		no strict 'refs';
		*{"$from\::$Declarator"} = sub (&$) {
            my $target = pop;
			unshift @_, ($Mapping, $Copula, {});
			my (@o) = uplevel 1, \&Object::Declare::_declare, @_;
            my $action = $o[-1]; # maybe loop through actions?
            $action->$Method($target);
		};
		*{"$from\::attempt"} = sub (&$) {
            my ($code, $element) = @_;
            eval { $code->() };
            my $e = $@;
            FIT::Document::Transformer::Error->
                new(description => $e)->
                _transform($element)
                    if $e;
            return $e ne ''; # 1 on error, 0 on no error
        };
	}
	goto &Object::Declare::import;
}


=head1 NAME

FIT::Document::Transformer - transform a Twig element


=head1 SYNOPSIS

 use FIT::Document::Transformer;

 my $cell = ...; # get a cell from some table, now lets transform it

 transform { Void  } $cell;                       # do nothing
 transform { Skip  } $cell;                       # mark as skipped (grayed out)
 transform { Right } $cell;                       # mark as right (green)
 transform { Right actual was 'foo' } $cell;      # same, plus add actual value
 transform { Wrong } $cell;                       # mark wrong (red)
 transform { Wrong actual was 'bar' } $cell;      # same, plus add actual value
 transform { Error description was 'eek' } $cell; # make error (yellow)

 # add 2 rows to table
 transform { AddRows rows are [ [1, 2], [3, 4] ] } $table;

 # or put logic inside the transform closure:
 transform
    { $actual eq $expected? Right: Wrong actual was $actual }
    $cell;
 
 # attempt to run some code, mark given element as Error and return
 # true only if code dies
 attempt { die 'bye!'  } $cell; # make cell yellow, annotate with error
 attempt { print "hi!' } $cell; # don't mark the cell with anything


=head1 DESCRIPTION

This package exports two functions, C<transform()>, and C<attempt()>. Both
accept a code reference and a Twig element.

Use C<transform()> to transform the XML of an element.

Use C<attempt()> to run code, trap any errors, and mark them in the element
that was given.

TODO: apply multiple transforms in one block

=cut



